package org.aldar.bquest.logic.controller.entity;

import org.aldar.bquest.constants.IConstants;
import org.aldar.bquest.constants.TileState;
import org.aldar.bquest.effects.SymbolAnimationUtil;
import org.aldar.bquest.entity.AbilityTileSprite;
import org.aldar.bquest.entity.CrystalTileSprite;
import org.aldar.bquest.entity.TileSprite;
import org.aldar.bquest.entity.TileSpriteUtil;
import org.aldar.bquest.entity.pool.TilesPool;
import org.aldar.bquest.logic.controller.board.MatchedTilesFinder;
import org.aldar.bquest.logic.controller.board.TouchAndMoveController;
import org.aldar.bquest.logic.controller.board.transform.TileBoardTransform;
import org.aldar.bquest.profile.GameProfile;
import org.aldar.bquest.resources.ResourceManager;
import org.aldar.bquest.screens.MainBoardScreen;
import org.aldar.bquest.util.Logger;
import org.andengine.entity.Entity;
import org.andengine.entity.IEntity;
import org.andengine.entity.modifier.*;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.entity.sprite.Sprite;
import org.andengine.util.math.MathUtils;
import org.andengine.util.modifier.IModifier;

import java.util.*;

import static org.aldar.bquest.entity.effect.ParticleUtil.showStarRotationEffect;

/**
 * User: Daria
 * Date: 4/14/13 10:18 PM
 */
public class TileBoard implements IConstants {

    private static Logger LOG = new Logger(TileBoard.class);

    public static HashMap<String, TileSprite> tileMap;

    private static List<String> crystalTileKeys = new LinkedList<String>();
    public static Set<String> tilesToReveal = null;

    public static Set<String> tilesInAction = new HashSet<String>();
    private static Entity enemyFlyingToTarget = null;

    public static void resetState() {
        tilesInAction = new HashSet<String>();
        tilesToReveal = new HashSet<String>();
        crystalTileKeys = new LinkedList<String>();
    }

    public static boolean isCellValid(int col, int row) {
        if (col >= CELLS_HORIZONTAL || col < 0 || row >= CELLS_VERTICAL || row < 0) {
            return false;
        }
        return MAP_FILLING[col][row] == 1 ? true : false;
    }

    public static void initTiles(boolean onLevelStart) {
        TouchAndMoveController.lastMoveWasMatch = false; //to prevent subtract mana on board rebuild!
        TileBoard.tileMap = new HashMap<String, TileSprite>();
        TileBoard.tilesToReveal = new HashSet<String>();
        int index = 0;
        for (int k = 0; k < 19; k++)
            for (int ii = 0; ii < IConstants.CELLS_HORIZONTAL; ii++) {
                for (int jj = IConstants.CELLS_VERTICAL - 1; jj >= 0; jj--) {
                    int i, j;
                    if (MathUtils.isEven(k)) {
                        i = ii;
                        j = jj;
                    } else {
                        i = jj;
                        j = ii;
                    }
                    if (isCellValid(i, j) && i + j == k) {
                        String key = getKey(j, i);
                        TileSprite tile = TileSpriteUtil.getRandomTile(j, i, 0);
                        tileMap.put(key, tile);
                        while (MatchedTilesFinder.isTileInMatch(i, j)) {
                            TileSpriteUtil.releaseTile(tile);
                            tile = TileSpriteUtil.getRandomTile(j, i, 0);
                            tileMap.put(key, tile);
                        }
                        tilesToReveal.add(key);
                        tile.setState(TileState.STATE_TOREVEAL);
                        tile.attachAndReveal(i, j, index++, onLevelStart);
                    }
                }
            }
    }

    private static int getRow(String key) {
        return Integer.parseInt(key.substring(1, 2));
    }

    private static int getCol(String key) {
        return Integer.parseInt(key.substring(0, 1));
    }

    public static synchronized void attachMagicTile() {
        List<String> keys = getCrystalTileKeys();
        LOG.i("Attaching magic tiles!");
        for (String key : keys) {
            String tileKey = key.substring(0, key.indexOf("_"));
            boolean isAbility = Boolean.parseBoolean(key.substring(key.indexOf("_") + 1));
            LOG.i("Key: " + key + " TileKey: " + tileKey + " isAbility: " + isAbility);
            int col = getCol(tileKey);
            int row = getRow(tileKey);
            TileSprite newTile = null;
            if (isAbility) {
                MainBoardScreen.getInstance().getGameSound().playAbilityAppearSound();
                if (GameProfile.getCurrentLevel().getAbilityTypes().length > 0) {
                    newTile = TileSpriteUtil.getAbilityTile(row, col);

                    showStarRotationEffect(1f, TileSpriteUtil.getTileCenterX(newTile.getTile().getX()),
                            TileSpriteUtil.getTileCenterY(newTile.getTile().getY()) + 5, newTile.getEffectContainer());

                } else {
                    newTile = TileSpriteUtil.getBigCrystalTile(row, col);
                }
                Sprite bigCrystalSymbol = TilesPool.getEntityForTexture(ResourceManager.getInstance().getSymbolBigCrystal());
                SymbolAnimationUtil.moveSymbolUp(newTile.getTile().getX(), newTile.getTile().getY(), bigCrystalSymbol);

            } else {
                newTile = TileSpriteUtil.getMagicTile(row, col);
            }
            tileMap.get(tileKey).detachFromLayer();
            tileMap.put(tileKey, newTile);
            newTile.attachToLayer(MainBoardScreen.getScm().getJwLayer()[col][row]);
            //newTile.getTile().registerEntityModifier(new AlphaModifier(0.5f, 0, 1));
            final TileSprite finalNewTile = newTile;
            newTile.getLsSprite().registerEntityModifier(
                    new AlphaModifier(0.3f, 0, 1, new IEntityModifier.IEntityModifierListener() {
                        @Override
                        public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                        }

                        @Override
                        public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                            if (finalNewTile instanceof AbilityTileSprite) {
                                AbilityTileSprite abilityTile = (AbilityTileSprite) finalNewTile;
                            }
                            if (finalNewTile instanceof CrystalTileSprite) {
                            }
                        }
                    }));
        }
        crystalTileKeys.clear();
    }

    public static void replaceAllAbilitiesForLandscape(boolean isTown) {
        for (int row = 0; row < IConstants.CELLS_VERTICAL; row++) {
            for (int col = 0; col < IConstants.CELLS_HORIZONTAL; col++) {
                if (TileBoard.isCellValid(col, row)) {
                    {
                        TileSprite tile = TileBoard.tileMap.get(TileBoard.getKey(row, col));
                        if (tile instanceof AbilityTileSprite) {
                            AbilityTileSprite abilityTile = (AbilityTileSprite) tile;
                            if (abilityTile.getAbilityType() == AbilityTileSprite.AbilityType.PLAY_CONCERT && !isTown) {
                                //set back original abilities!
                                AbilityTileSprite.AbilityType newAbilityType = abilityTile.getOriginalAbility();
                                if (newAbilityType == null) {
                                    //ability tile appeared when it was town landscape
                                    newAbilityType = AbilityTileSprite.getRandomAbilityTypeNotConcert();
                                }
                                abilityTile.replaceAbility(newAbilityType);
                            } else if (isTown && abilityTile.getAbilityType() != AbilityTileSprite.AbilityType.PLAY_CONCERT) {
                                abilityTile.replaceAbility(AbilityTileSprite.AbilityType.PLAY_CONCERT);
                            }
                        }
                    }
                }
            }
        }
    }

    public static String getKey(final int row, final int col) {
        if (row < CELLS_VERTICAL && col < CELLS_HORIZONTAL) {
            return String.valueOf(col) + String.valueOf(row);
        } else {
            return "";
        }
    }

    public static void swapInHashMap(int sourceCol, int sourceRow, int targetCol, int targetRow) {
        //HashMap里互换
        tileMap.get(getKey(targetRow, targetCol)).swapLayer(MainBoardScreen.getScm().getJwLayer()[targetCol][targetRow], MainBoardScreen.getScm().getJwLayer()[sourceCol][sourceRow]);
        tileMap.get(getKey(sourceRow, sourceCol)).swapLayer(MainBoardScreen.getScm().getJwLayer()[sourceCol][sourceRow], MainBoardScreen.getScm().getJwLayer()[targetCol][targetRow]);


        TileSprite temp = tileMap.get(getKey(sourceRow, sourceCol));
        tileMap.remove(getKey(sourceRow, sourceCol));
        TileSprite targetTile = tileMap.get(getKey(targetRow, targetCol));
        tileMap.put(getKey(sourceRow, sourceCol), targetTile);
        targetTile.setCurrentCol(sourceCol);
        targetTile.setCurrentRow(sourceRow);

        tileMap.remove(getKey(targetRow, targetCol));
        tileMap.put(getKey(targetRow, targetCol), temp);
        temp.setCurrentRow(targetRow);
        temp.setCurrentCol(targetCol);

    }

    public static int getAdjacence(int newCol, int newRow) {
        if (newCol == TouchAndMoveController.sourceCol && newRow == TouchAndMoveController.sourceRow) {
            return NON_ADJACENT_SAME;
        }
        if (Math.abs(newRow - TouchAndMoveController.sourceRow) == 1 && newCol == TouchAndMoveController.sourceCol) {
            return ADJACENT_VERTICAL;
        }
        if (Math.abs(newCol - TouchAndMoveController.sourceCol) == 1 && newRow == TouchAndMoveController.sourceRow) {
            return ADJACENT_HORIZONTAL;
        }
        return NON_ADJACENT;
    }

    public static HashMap<String, TileSprite> getTileMap() {
        return tileMap;
    }

    public static TileSprite getSourceTile() {
        TileSprite sprite = getTileMap().get(getKey(TouchAndMoveController.sourceRow, TouchAndMoveController.sourceCol));
        return sprite;
    }

    public static List<String> getCrystalTileKeys() {
        return crystalTileKeys;
    }

    public static void setAllTilesToDestroyedState(Runnable afterDestroyActionFinished, LinkedList<TileSprite> spritesInAction) {
        TouchAndMoveController.DONT_TOUCH = true;
        for (int row = 0; row < IConstants.CELLS_VERTICAL; row++) {
            for (int col = 0; col < IConstants.CELLS_HORIZONTAL; col++) {
                if (TileBoard.isCellValid(col, row)) {
                    {
                        final TileSprite tile = TileBoard.tileMap.get(TileBoard.getKey(row, col));
                        if (tile.getEnemyImp() != null) {
                            final Entity enemyImpCopy = tile.getEnemyImp();
                            tile.setEnemyImp(null);
                            enemyImpCopy.registerEntityModifier(new ScaleModifier(0.2f, 1f, 2f, new IEntityModifier.IEntityModifierListener() {
                                @Override
                                public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                }

                                @Override
                                public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                    enemyImpCopy.registerEntityModifier(new ScaleModifier(0.3f, 2f, 0f, new IEntityModifier.IEntityModifierListener() {
                                        @Override
                                        public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                        }

                                        @Override
                                        public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                            MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                                                @Override
                                                public void run() {
                                                    enemyImpCopy.detachSelf();
                                                }
                                            });
                                        }
                                    }));
                                }
                            }));

                        } else if (tile.getFireOnTile() != null) {
                            final Entity fireOnTileCopy = tile.getFireOnTile();
                            tile.setFireOnTile(null); //set null now to prevent starting enemy actions!!
                            fireOnTileCopy.registerEntityModifier(new ScaleModifier(0.2f, 1f, 3f, new IEntityModifier.IEntityModifierListener() {
                                @Override
                                public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                }

                                @Override
                                public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                    fireOnTileCopy.registerEntityModifier(new ScaleModifier(0.2f, 3f, 0f, new IEntityModifier.IEntityModifierListener() {
                                        @Override
                                        public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                        }

                                        @Override
                                        public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                            MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                                                @Override
                                                public void run() {
                                                    fireOnTileCopy.detachSelf();
                                                }
                                            });
                                        }
                                    }));
                                }
                            }));
                        }
                        tile.doDestroy(afterDestroyActionFinished, spritesInAction);
                    }
                }
            }
        }
    }

    public static void recreateHopelessTileBoard() {
        TouchAndMoveController.lockTouchActions();

        if (enemyFlyingToTarget != null) {
            enemyFlyingToTarget.clearEntityModifiers();
            enemyFlyingToTarget.detachSelf();
            enemyFlyingToTarget = null;
        }
        final Sprite noMovesText = TilesPool.getEntityForTexture(ResourceManager.getInstance().getNoMovesLeftMessage());
        noMovesText.setAnchorCenter(0, 0);
        MainBoardScreen.getScm().getBonusLayer().attachChild(noMovesText);
        noMovesText.registerEntityModifier(new SequenceEntityModifier(
                new AlphaModifier(0.5f, 0, 1),
                new DelayModifier(0.5f),
                new AlphaModifier(0.5f, 1, 0, new IEntityModifier.IEntityModifierListener() {
                    @Override
                    public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                        setAllTilesToDestroyedState(new Runnable() {
                            @Override
                            public void run() {
                                TileBoard.initTiles(false);
                            }
                        }, new LinkedList<TileSprite>());
                    }

                    @Override
                    public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                        MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                            @Override
                            public void run() {
                                noMovesText.detachSelf();
                                TilesPool.pushEntityToStack(noMovesText);
                            }
                        });
                    }
                })
        ));
    }

    public static void setEnemyOnBoard(final AnimatedSprite enemyImpAccounter) {
        TileBoard.enemyFlyingToTarget = enemyImpAccounter;
        int randomKeyIndex = new Random().nextInt(tileMap.keySet().size());
        final String randomKey = new ArrayList<String>(tileMap.keySet()).get(randomKeyIndex);
        int row = getRow(randomKey);
        int col = getCol(randomKey);
        LOG.i("Chosen random tile key to enemy attack!! key is: " + randomKey + " row " + row + " col " + col);
        float[] tileXY =
                TileBoardTransform.getSpriteAdjustedScreenCoords(TileBoardTransform.getScreenCoords(
                        col * CELL_WIDTH,
                        row * CELL_HEIGHT));

        final float[] enemyXY = getEnemyCoordsFromTileCoords(tileXY);


        /*Rectangle rectNewTileXY = new Rectangle(enemyXY[0], enemyXY[1], 5, 5, ScreenContentManager.getScreenContentManager().getVertexBufferObjectManager());
        rectNewTileXY.setAnchorCenter(0, 0);
        rectNewTileXY.setColor(Color.GREEN);
        ScreenContentManager.getScreenContentManager().getTestLayer().attachChild(rectNewTileXY);
        */
        float distance = MathUtils.distance(enemyImpAccounter.getX(), enemyImpAccounter.getY(),
                enemyXY[0], enemyXY[1]);
        enemyImpAccounter.registerEntityModifier(new MoveModifier(distance * 0.005f, enemyImpAccounter.getX(), enemyImpAccounter.getY(),
                enemyXY[0], enemyXY[1], new IEntityModifier.IEntityModifierListener() {
            @Override
            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {

            }

            @Override
            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                TileSprite tileToAttack = tileMap.get(randomKey);
                if (/*tileToAttack.getStyle().getBaseType() == LandscapeType.CRYSTAL_BASE   //crystal tiles cannot be attacked by enemy!
                        ||*/ tileToAttack.getEnemyImp() != null
                        || tileToAttack.getFireOnTile() != null) {
                    LOG.i("Cannot set enemy imp on tile - tile has already imp or fire upon it. Will retry another tile..");
                    setEnemyOnBoard(enemyImpAccounter);
                } else {
                    LOG.i("Found tile to set enemy imp on! Key is: " + randomKey);
                    MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                        @Override
                        public void run() {
                            enemyImpAccounter.detachSelf();
                        }
                    });
                    tileToAttack.enableEnemyImp();
                    enemyFlyingToTarget = null;
                }
                ;
            }
        }));

    }

    public static float[] getEnemyCoordsFromTileCoords(float[] tileXY) {
        float[] enemyXY = new float[2];
        enemyXY[0] = tileXY[0] + 30;
        enemyXY[1] = tileXY[1] - 70;
        return enemyXY;
    }

    public static float[] getFireCoordsFromTileCoords(float[] tileXY) {
        float[] fireXY = new float[2];
        fireXY[0] = tileXY[0] + 40;
        fireXY[1] = tileXY[1] - 20;
        return fireXY;
    }

}
